/*
 * ASTPrinter.h
 *
 *  Created on: Nov 9, 2010
 *      Author: dhovemey
 */

#ifndef ASTPRINTER_H_
#define ASTPRINTER_H_

#include <vector>
#include <cstddef>
#include <cassert>
#include <cstdio>
#include "fe/ASTNode.h"
#include "fe/Import.h"

class ASTPrinterItem {
private:
	ASTNode* node;
	size_t remainingChildren;
	bool visited;

public:
	ASTPrinterItem(ASTNode* node_)
		: node(node_)
		, remainingChildren(node_->getChildList().size())
		, visited(false)
	{
	}

	bool isVisited() { return visited; }

	void setVisited(bool visited_) { visited = visited_; }

	ASTNode* getNode() { return node; }

	size_t getRemainingChildren() { return remainingChildren; }

	ASTNode* nextChild() {
		assert(remainingChildren > 0);
		ASTNode* child = node->getChildList().at(node->getChildList().size() - remainingChildren);
		--remainingChildren;
		return child;
	}
};

class ASTPrinter {
private:
	std::vector<ASTPrinterItem*> stack;

public:
	ASTPrinter() { }

	void print(ASTNode* node) {
		stack.push_back(new ASTPrinterItem(node));

		while (!stack.empty()) {
			ASTPrinterItem *item = stack.back();
			stack.pop_back();
			if (!item->isVisited()) {
				printNode(item->getNode());
				item->setVisited(true);
			}
			if (item->getRemainingChildren() > 0) {
				stack.push_back(item);
				stack.push_back(new ASTPrinterItem(item->nextChild()));
			}
		}
	}

private:
	void printNode(ASTNode* node) {
		for (size_t i = 0; i < stack.size(); i++) {
			bool parentLevel = (i == stack.size() - 1);
			if (parentLevel) {
				printf("+--");
			} else {
				printf("%s", stack.at(i)->getRemainingChildren() > 0 ? "|  " : "   ");
			}
		}

		printf("%ls", ASTNode::getAstNodeTypeName(node->getType()).c_str());
		if (node->getType() == ASTNode::AST_BINARY_OP_EXPR) {
			printf("(%ls)", Token2::getBinOpName(Token2::TokenType(node->getIntValue())).c_str());
		} else if (node->isFlagSet(ASTFLAG_HAS_STRING_VALUE)) {
			printf("(%ls)", node->getStringValue().c_str());
		} else if (node->isFlagSet(ASTFLAG_HAS_INT_VALUE)) {
			printf("(%d)", node->getIntValue());
		} else if (node->isFlagSet(ASTFLAG_HAS_IMPORT)) {
			const Import *import = node->getImport();
			if (import->isStatic()) {
				printf("(%ls method %ls)", import->getImportClsName().c_str(), import->getAlias().c_str());
			} else {
				printf("(%ls as %ls)", import->getImportClsName().c_str(), import->getAlias().c_str());
			}
		}
		printf("\n");
	}
};

#endif /* ASTPRINTER_H_ */
